﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Othello_Game
{
    public class MinMaxTreeSearch : IReinforcementLearningAlgorithm
    {
        #region Public Properties
        /// <summary>
        /// Gets and sets the Alpha value of this current move
        /// </summary>
        public double AlphaValue { get; private set; }

        /// <summary>
        /// Gets and sets the Beta value of this current move
        /// </summary>
        public double BetaValue { get; private set; }

        #endregion

        public static int notFirst = 0;

        /// <summary>
        /// OLD CODE!! 
        /// Not in use.
        /// </summary>
        public ActionList FindBest(Othello_Game.Board board, int depthSearch)
        {
            if (board.WhatCanIDo.Count() > 0)
            {
                if ((GeneralConsts.random.Next(int.MaxValue) % 10) == 1)
                {
                    return board.WhatCanIDo[GeneralConsts.random.Next(int.MaxValue) % board.WhatCanIDo.Count()];
                }
            }

            ActionList toDo = null;
            board.WhatCanIDo.Sort(GeneralConsts.Instance.ActionsListComparer);

            foreach (ActionList move in board.WhatCanIDo)
            {
                if (board.countEmptySquares() <= 14) depthSearch = 5;
                double alpha = Math.Max(double.NegativeInfinity, -SearchAndPruneMoves(move, depthSearch, double.NegativeInfinity, double.PositiveInfinity));
                if (toDo == null || toDo.AlphaValue < move.AlphaValue)
                {
                    toDo = move;
                }
            }

            return toDo;
        }

        /// <summary> 
        /// Return the best move to play  
        /// </summary> 
        /// <param name="board">Describes the board game state</param> 
        /// <param name="depthSearch">The depth of the search</param> 
        /// <param name="ms">A main screen object</param> 
        /// <returns></returns> 
        public ActionList FindBest(Othello_Game.Board board, int depthSearch, MainScreen ms)
        {
            // If found possible moves to play 
            if (board.WhatCanIDo.Count() > 0)
            {
                // Add a randomized factor to the strategy 
                if ((GeneralConsts.random.Next(int.MaxValue) % 10) == 1)
                {
                    return board.WhatCanIDo[GeneralConsts.random.Next(int.MaxValue) % board.WhatCanIDo.Count()];
                }
            }

            ActionList toDo = null;

            // Init the ProgressBar 
            ms.SetProgressBarLimit(board.WhatCanIDo.Count());
            int i = 0;
            int depth = 0;
            ms.SetProgressBar(i);

            // Sort the moves list by their values.  
            board.WhatCanIDo.Sort(GeneralConsts.Instance.ActionsListComparer);
            if (MainScreen.UseMiniMax)
            {
                foreach (ActionList move in board.WhatCanIDo)
                {
                    i++;
                    // If possible moves is less then 5, search depth set to 7 
                    if (board.WhatCanIDo.Count() <= 5) depth = 7;
                    // If possible moves is less then 9, search depth set to 5 
                    else if (board.WhatCanIDo.Count() <= 9) depth = 5;
                    // If possible moves is more then 10, search depth set to 3 
                    else depth = 3;
                    if (board.countEmptySquares() <= 14) depth = 7;

                    //Initially alpha is negative infinity and beta is positive infinity 
                    double alpha = Math.Max(double.NegativeInfinity, -SearchAndPruneMoves(move, depth, double.NegativeInfinity, double.PositiveInfinity));
                    if (toDo == null || toDo.AlphaValue < move.AlphaValue)
                    {
                        toDo = move;
                    }

                    // Update the ProgressBar
                    ms.SetProgressBar(i);
                }
            }

            // Return the move with the highest alpha value (first one), meaning that move will
            // give us the highest minimal profits for the max player (the best moves)
            else return board.WhatCanIDo[0];

            return toDo;
        }

        /// <summary>
        /// This is a recursive Implementation of the Alpha-Beta Pruning of a Min-Max Tree. 
        /// This algorithm searches down a possible move, calculates the profit value of the move, 
        /// and prune moves and paths using the alpha & beta values.
        /// </summary>
        /// <param name="moveToPlay">A move we search down from</param>
        /// <param name="depthSearch">The depth of the search</param>
        /// <param name="alpha">Represent the minimal value expected for Max-player</param>
        /// <param name="beta">Represent the maximal value expected for Min-player</param>
        /// <returns>The searched move value</returns>
        private double SearchAndPruneMoves(ActionList moveToPlay, int depthSearch, double alpha, double beta)
        {
            // If we're done searching this branch
            if ((depthSearch == 0) || (moveToPlay.Actions.Count() == 0))
            {
                // return the move's Alpha value
                return moveToPlay.AlphaValue;
            }
            // Else we search the move's children
            else
            {
                // Create a new copy of the board state
                Board newBoard = moveToPlay.BoardState.Copy();

                // We play the father move, switch players and look for the children moves
                newBoard.Play(moveToPlay);
                newBoard.SwitchPlayers();
                List<ActionList> moveChilds = newBoard.WhatCanIDo;

                // Sort the move's children list by their values. 
                // We want to first try test the moves likely to give us higher profits.
                moveChilds.Sort(GeneralConsts.Instance.ActionsListComparer);

                foreach (ActionList child in moveChilds)
                {
                    // Switching between the alpha and beta values for the opponent's moves
                    alpha = Math.Max(alpha, -SearchAndPruneMoves(child, depthSearch - 1, -beta, -alpha));

                    // Beta cutoff 
                    if (beta <= alpha)
                    {
                        break;
                    }
                }

                return alpha;
            }
        }
    }
}
